﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Json;
using Xmart.Infrastructures.Office.Helpers;
using Xms.Abp.Application.Dtos;
using Xms.Abp.Authorization;
using Xms.Abp.Data;
using Xms.Abp.Domain;
using Xms.Abp.Domain.Repositories;
using Xms.Abp.Features;
using Xms.Abp.Identity;
using Xms.Abp.Linq;
using Xms.Abp.ObjectMapping;
using Xms.Abp.OperationLogging;

namespace Xms.Abp.Application.Services;

public abstract class XmsCrudAppService<TEntity, TEntityDto, TKey>
    : XmsCrudAppService<TEntity, TEntityDto, TKey, PagedSortedAndFilterParameter>,
    IXmsCrudAppService<TEntityDto, TKey>
    where TEntity : class, IEntity<TKey>
    where TEntityDto : IEntityDto<TKey>
{
    protected XmsCrudAppService(IXmsRepository<TEntity, TKey> repository) : base(repository)
    {

    }
}

public abstract class XmsCrudAppService<TEntity, TEntityDto, TKey, TPagedParameter>
    : XmsCrudAppService<TEntity, TEntityDto, TKey, TPagedParameter, TEntityDto>,
    IXmsCrudAppService<TEntityDto, TKey, TPagedParameter>
    where TEntity : class, IEntity<TKey>
    where TEntityDto : IEntityDto<TKey>
    where TPagedParameter : class, IPagedSortedAndFilterParameter
{
    protected XmsCrudAppService(IXmsRepository<TEntity, TKey> repository)
        : base(repository)
    {

    }
}

public abstract class XmsCrudAppService<TEntity, TEntityDto, TKey, TPagedParameter, TCreateParameter>
    : XmsCrudAppService<TEntity, TEntityDto, TKey, TPagedParameter, TCreateParameter, TCreateParameter>,
    IXmsCrudAppService<TEntityDto, TKey, TPagedParameter, TCreateParameter>
    where TEntity : class, IEntity<TKey>
    where TEntityDto : IEntityDto<TKey>
    where TPagedParameter : class, IPagedSortedAndFilterParameter
{
    protected XmsCrudAppService(IXmsRepository<TEntity, TKey> repository)
        : base(repository)
    {

    }
}

public abstract class XmsCrudAppService<TEntity, TEntityDto, TKey, TPagedParameter, TCreateParameter, TUpdateParameter>
    : XmsCrudAppService<TEntity, TEntityDto, TEntityDto, TKey, TPagedParameter, TCreateParameter, TUpdateParameter>,
    IXmsCrudAppService<TEntityDto, TKey, TPagedParameter, TCreateParameter, TUpdateParameter>
    where TEntity : class, IEntity<TKey>
    where TEntityDto : IEntityDto<TKey>
    where TPagedParameter : class, IPagedSortedAndFilterParameter
{
    protected XmsCrudAppService(IXmsRepository<TEntity, TKey> repository)
        : base(repository)
    {

    }

    protected override Task<TEntityDto> MapToGetListOutputDtoAsync(TEntity entity)
    {
        return MapToGetOutputDtoAsync(entity);
    }

    protected override TEntityDto MapToGetListOutputDto(TEntity entity)
    {
        return MapToGetOutputDto(entity);
    }
}

public abstract class XmsCrudAppService<TEntity, TDetailsResult, TPagingListResult, TKey, TPagedParameter, TCreateParameter, TUpdateParameter>
    : XmsCrudAppService<TEntity, TDetailsResult, TPagingListResult, TDetailsResult, TKey, TPagedParameter, TCreateParameter, TUpdateParameter>,
    IXmsCrudAppService<TDetailsResult, TPagingListResult, TKey, TPagedParameter, TCreateParameter, TUpdateParameter>
    where TEntity : class, IEntity<TKey>
    where TDetailsResult : IEntityDto<TKey>
    where TPagingListResult : IEntityDto<TKey>
    where TPagedParameter : class, IPagedSortedAndFilterParameter
{
    protected XmsCrudAppService(IXmsRepository<TEntity, TKey> repository)
        : base(repository)
    {
    }
}

public abstract class XmsCrudAppService<TEntity, TDetailsResult, TPagingListResult, TExportResult, TKey, TPagedParameter, TCreateParameter, TUpdateParameter>
    : XmsCrudAppService<TEntity, TDetailsResult, TPagingListResult, TExportResult, TExportResult, TKey, TPagedParameter, TCreateParameter, TUpdateParameter>,
    IXmsCrudAppService<TDetailsResult, TPagingListResult, TExportResult, TKey, TPagedParameter, TCreateParameter, TUpdateParameter>
    where TEntity : class, IEntity<TKey>
    where TDetailsResult : IEntityDto<TKey>
    where TPagingListResult : IEntityDto<TKey>
    where TPagedParameter : class, IPagedSortedAndFilterParameter
{
    protected XmsCrudAppService(IXmsRepository<TEntity, TKey> repository)
        : base(repository)
    {
    }
}

public abstract class XmsCrudAppService<TEntity, TDetailsResult, TPagingListResult, TExportResult, TImportParameter, TKey, TPagedParameter, TCreateParameter, TUpdateParameter>
    : CrudAppService<TEntity, TDetailsResult, TPagingListResult, TKey, TPagedParameter, TCreateParameter, TUpdateParameter>,
    IXmsCrudAppService<TDetailsResult, TPagingListResult, TExportResult, TImportParameter, TKey, TPagedParameter, TCreateParameter, TUpdateParameter>
    where TEntity : class, IEntity<TKey>
    where TDetailsResult : IEntityDto<TKey>
    where TPagingListResult : IEntityDto<TKey>
    where TPagedParameter : class, IPagedSortedAndFilterParameter
{
    protected IOperationLoggingManager OperationLoggingManage => LazyInject<IOperationLoggingManager>();
    protected IJsonSerializer JsonSerializer => LazyInject<IJsonSerializer>();
    private IQueryableExecuter _QueryableExecuter;
    protected IQueryableExecuter QueryableExecuter
    {
        get
        {
            if (_QueryableExecuter == null)
            {
                _QueryableExecuter = AsyncExecuter as IQueryableExecuter;
                if (_QueryableExecuter == null)
                {
                    _QueryableExecuter = LazyInject<IQueryableExecuter>();
                }
            }
            return _QueryableExecuter;
        }
    }

    protected virtual int ExportMaxResultCount => 10000;
    protected virtual bool ExportDesensitization => false;
    protected virtual bool PageListDesensitization => true;

    protected new IXmsObjectMapper ObjectMapper => base.ObjectMapper as IXmsObjectMapper;
    protected new IFeatureChecker FeatureChecker => LazyInject<IFeatureChecker>();

    protected virtual Func<TEntity, (string operation, string message)> CreateOperationLogCreator { get; }
    protected virtual Func<TEntity, TEntity, (string operation, string message)> UpdateOperationLogCreator { get; }
    protected virtual Func<IEnumerable<TEntity>, (string operation, string message)> DeleteOperationLogCreator { get; }
    protected virtual Func<TEntity, (string operation, string message)> ToggleEnableOperationLogCreator { get; }
    protected virtual Func<TCreateParameter, TEntity> EntityCreator { get; }
    protected virtual async Task BeforeCreateAsync(TEntity entity, TCreateParameter input)
    {
        if (entity is IHasReview reviewEntity)
        {
            reviewEntity.ReviewStatus = ReviewStatus.Draft;
        }
        await Task.CompletedTask;
    }
    protected virtual async Task BeforeUpdateAsync(TEntity entity, TUpdateParameter input)
    {
        if (entity is IHasReview reviewEntity)
        {
            if (reviewEntity.ReviewStatus == ReviewStatus.Reviewing)
            {
                throw new UserFriendlyException("审核中状态数据不能修改");
            }
            if (reviewEntity.ReviewStatus == ReviewStatus.Approved)
            {
                throw new UserFriendlyException("审核通过状态数据不能修改");
            }
            reviewEntity.ReviewStatus = ReviewStatus.Draft;
        }
        await Task.CompletedTask;
    }
    protected virtual async Task BeforeSubmitReviewAsync(TEntity entity)
    {
        await Task.CompletedTask;
    }
    protected virtual async Task BeforeReviewAsync(TEntity entity, ReviewParameter parameter)
    {
        await Task.CompletedTask;
    }
    protected virtual async Task BeforeImportAsync(List<TEntity> entities, List<TImportParameter> imports)
    {
        await Task.CompletedTask;
    }
    protected virtual async Task BeforeToggleEnableAsync(TEntity entity, bool input)
    {
        if (entity is IHasReview reviewEntity)
        {
            if (reviewEntity.ReviewStatus != ReviewStatus.Approved)
            {
                throw new UserFriendlyException("审核通过后才能启用禁用");
            }
        }
        await Task.CompletedTask;
    }
    protected virtual async Task BeforeDeleteAsync(TEntity entity)
    {
        if (entity is IHasReview reviewEntity)
        {
            if (reviewEntity.ReviewStatus == ReviewStatus.Approved)
            {
                throw new UserFriendlyException("审核通过后不能删除");
            }
        }
        await Task.CompletedTask;
    }
    protected virtual async Task AfterGetAsync(TEntity entity)
    {
        await Task.CompletedTask;
    }
    protected virtual async Task AfterCreateAsync(TEntity entity, TCreateParameter input)
    {
        await Task.CompletedTask;
    }
    protected virtual async Task AfterUpdateAsync(TEntity entity, TUpdateParameter input)
    {
        await Task.CompletedTask;
    }
    protected virtual async Task AfterSubmitReviewAsync(TEntity entity)
    {
        await Task.CompletedTask;
    }
    protected virtual async Task AfterReviewAsync(TEntity entity, ReviewParameter parameter)
    {
        await Task.CompletedTask;
    }
    protected virtual async Task AfterImportAsync(List<TEntity> entities, List<TImportParameter> imports)
    {
        await Task.CompletedTask;
    }
    protected virtual async Task AfterToggleEnableAsync(TEntity entity, bool input)
    {
        await Task.CompletedTask;
    }
    protected virtual async Task AfterDeleteAsync(TEntity entity)
    {
        await Task.CompletedTask;
    }
#pragma warning disable CS0809
    [Obsolete($"Use {nameof(DetailsPolicyName)}.")]
    protected sealed override string GetPolicyName => DetailsPolicyName;
    [Obsolete($"Use {nameof(PageListPolicyName)}.")]
    protected sealed override string GetListPolicyName => PageListPolicyName;
#pragma warning restore CS0809
    protected virtual string PageListPolicyName => GetPolicy();
    protected virtual string DetailsPolicyName => GetPolicy();
    protected override string CreatePolicyName => GetPolicy();
    protected override string UpdatePolicyName => GetPolicy();
    protected override string DeletePolicyName => GetPolicy();
    protected virtual string DeleteManyPolicyName => GetPolicy();
    protected virtual string ToggleEnablePolicyName => GetPolicy();
    protected virtual string ExportPolicyName => GetPolicy();
    protected virtual string ImportPolicyName => GetPolicy();
    protected virtual string ReviewPolicyName => GetPolicy();
    protected virtual string SubmitReviewPolicyName => GetPolicy();
    protected virtual Dictionary<string, string> Policies { get; }
    protected virtual string GetPolicy([CallerMemberName] string memberName = null)
    {
        memberName = memberName.RemovePostFix("PolicyName").RemovePostFix("Async");
        return Policies.GetOrDefault(memberName);
    }

    protected new IXmsRepository<TEntity, TKey> Repository { get; }
    protected XmsCrudAppService(IXmsRepository<TEntity, TKey> repository)
        : base(repository)
    {
        Repository = repository;
        var type = this.GetType();
        var attr = type.GetCustomAttribute<PermissionsUseAttribute>();
        if (attr != null)
        {
            Policies = PermissionsConst.ToDictionary(attr.PermissionsConstType);
        }
        else
        {
            attr = type.GetCustomAttribute<DynamicPermissionsUseAttribute>();
            if (attr != null)
            {
                Policies = PermissionsConst.ToDictionary(attr.PermissionsConstType);
            }
            else
            {
                Policies = new();
            }
        }
    }

    public TService LazyInject<TService>()
    {
        return LazyServiceProvider.LazyGetRequiredService<TService>();
    }

    [Obsolete($"Use {nameof(LazyInject)} method.")]
    public TService Inject<TService>()
    {
#pragma warning disable CS0618 // 类型或成员已过时
        return ServiceProvider.GetRequiredService<TService>();
#pragma warning restore CS0618 // 类型或成员已过时
    }

#pragma warning disable CS0809
    [Obsolete($"Use {nameof(CheckPageListPolicyAsync)} method.")]
    protected override async Task CheckGetListPolicyAsync()
    {
        await CheckPolicyAsync(PageListPolicyName);
    }

    [Obsolete($"Use {nameof(CheckDetailsPolicyAsync)} method.")]
    protected override async Task CheckGetPolicyAsync()
    {
        await CheckPolicyAsync(DetailsPolicyName);
    }
#pragma warning restore CS0809

    protected virtual async Task CheckPageListPolicyAsync()
    {
        await CheckPolicyAsync(PageListPolicyName);
    }

    protected virtual async Task CheckDetailsPolicyAsync()
    {
        await CheckPolicyAsync(DetailsPolicyName);
    }

    protected virtual async Task CheckDeleteManyPolicyAsync()
    {
        await CheckPolicyAsync(DeleteManyPolicyName);
    }

    protected virtual async Task CheckToggleEnablePolicyAsync()
    {
        await CheckPolicyAsync(ToggleEnablePolicyName);
    }

    protected virtual async Task CheckExportPolicyAsync()
    {
        await CheckPolicyAsync(ExportPolicyName);
    }

    protected virtual async Task CheckImportPolicyAsync()
    {
        await CheckPolicyAsync(ImportPolicyName);
    }

    protected virtual async Task CheckSubmitReviewPolicyAsync()
    {
        await CheckPolicyAsync(SubmitReviewPolicyName);
    }

    protected virtual async Task CheckReviewPolicyAsync()
    {
        await CheckPolicyAsync(ReviewPolicyName);
    }

    protected virtual Task<TEntity> MapToEntityAsync(TCreateParameter createInput, TEntity entity)
    {
        return Task.FromResult(MapToEntity(createInput, entity));
    }

    protected virtual TEntity MapToEntity(TCreateParameter createInput, TEntity entity)
    {
        ObjectMapper.Map(createInput, entity);
        SetIdForGuids(entity);
        return entity;
    }
    protected virtual bool IsCheckNoUnique { get; } = true;
    protected virtual bool IsCheckNameUnique { get; }

    protected virtual async Task CheckCreateUniqueAsync(TCreateParameter input)
    {
        if (IsCheckNoUnique && input is IHasNo inputNo)
        {
            if (await (await Repository.GetQueryableAsync()).AnyAsync(r => (r as IHasNo).No == inputNo.No))
            {
                throw new UserFriendlyException("编号已存在");
            }
        }
        if (IsCheckNameUnique && input is IHasName inputName)
        {
            if (await (await Repository.GetQueryableAsync()).AnyAsync(r => (r as IHasName).Name == inputName.Name))
            {
                throw new UserFriendlyException("名称已存在");
            }
        }
    }

    public override async Task<TDetailsResult> CreateAsync(TCreateParameter input)
    {
        await CheckCreatePolicyAsync();

        await ValidateCreateAsync(input);

        await CheckCreateUniqueAsync(input);

        var entity = EntityCreator == null ? await MapToEntityAsync(input) : await MapToEntityAsync(input, EntityCreator(input));

        TryToSetTenantId(entity);

        await BeforeCreateAsync(entity, input);

        await Repository.InsertAsync(entity, autoSave: true);

        await AfterCreateAsync(entity, input);

        var result = await MapToGetOutputDtoAsync(entity);

        await FullingResultDetailsAsync(result);

        await AddCreateOperationLogAsync(entity);

        return result;
    }

    protected virtual async Task CheckUpdateUniqueAsync(TKey entityId, TUpdateParameter input)
    {
        if (IsCheckNoUnique && input is IHasNo inputNo)
        {
            if (await (await Repository.GetQueryableAsync()).AnyAsync(r => (r as IHasNo).No == inputNo.No && !r.Id.Equals(entityId)))
            {
                throw new UserFriendlyException("编号已存在");
            }
        }
        if (IsCheckNameUnique && input is IHasName inputName)
        {
            if (await (await Repository.GetQueryableAsync()).AnyAsync(r => (r as IHasName).Name == inputName.Name && !r.Id.Equals(entityId)))
            {
                throw new UserFriendlyException("名称已存在");
            }
        }
    }

    public override async Task<TDetailsResult> UpdateAsync(TKey id, TUpdateParameter input)
    {
        await CheckUpdatePolicyAsync();

        var entity = await GetEntityByIdAsync(id);

        await ValidateUpdateAsync(entity, input);

        await CheckUpdateUniqueAsync(entity.Id, input);

        var source = ObjectMapper.Map<TEntity, TEntity>(entity);

        await MapToEntityAsync(input, entity);
        if (entity is IHasConcurrencyStamp ent && ent.ConcurrencyStamp.IsNullOrWhiteSpace() && input is IHasConcurrencyStamp inp && !inp.ConcurrencyStamp.IsNullOrWhiteSpace())
        {
            ent.SetConcurrencyStampIfNotNull(inp.ConcurrencyStamp);
        }

        await BeforeUpdateAsync(entity, input);

        await Repository.UpdateAsync(entity, autoSave: true);

        await AfterUpdateAsync(entity, input);

        var result = await MapToGetOutputDtoAsync(entity);

        await FullingResultDetailsAsync(result);

        await AddUpdateOperationLogAsync(source, entity);

        return result;
    }

    public virtual async Task SubmitReviewAsync(TKey id)
    {
        if (!typeof(IHasReview).IsAssignableFrom(typeof(TEntity)))
        {
            throw new UserFriendlyException("数据不支持审核");
        }
        await CheckSubmitReviewPolicyAsync();

        var entity = await GetEntityByIdAsync(id);

        var reviewEntity = entity as IHasReview;
        if (reviewEntity.ReviewStatus != ReviewStatus.Draft)
        {
            throw new UserFriendlyException("草稿状态才能提交审核");
        }
        reviewEntity.ReviewStatus = ReviewStatus.Reviewing;

        await BeforeSubmitReviewAsync(entity);

        entity = await Repository.UpdateAsync(entity, autoSave: true);

        await AfterSubmitReviewAsync(entity);
    }

    public virtual async Task ReviewAsync(TKey id, ReviewParameter parameter)
    {
        if (!typeof(IHasReview).IsAssignableFrom(typeof(TEntity)))
        {
            throw new UserFriendlyException("数据不支持审核");
        }
        await CheckReviewPolicyAsync();

        var entity = await GetEntityByIdAsync(id);

        var reviewEntity = entity as IHasReview;
        if (reviewEntity.ReviewStatus != ReviewStatus.Reviewing)
        {
            throw new UserFriendlyException("审核中状态才能审核");
        }
        reviewEntity.ReviewStatus = parameter.IsApproved ? ReviewStatus.Approved : ReviewStatus.Reject;
        reviewEntity.ReviewTime = DateTime.Now;
        reviewEntity.ReviewerId = CurrentUser.Id;
        reviewEntity.ReviewRemark = parameter.ReviewRemark;

        await BeforeReviewAsync(entity, parameter);

        entity = await Repository.UpdateAsync(entity, autoSave: true);

        await AfterReviewAsync(entity, parameter);
    }

    public override async Task<TDetailsResult> GetAsync(TKey id)
    {
        await CheckGetPolicyAsync();

        var entity = await GetEntityByIdAsync(id);
        await AfterGetAsync(entity);
        var result = await MapToGetOutputDtoAsync(entity);
        try
        {
            await FullingResultDetailsAsync(result);
            return result;
        }
        catch
        {
            return default;
        }
    }

    public virtual async Task<TDetailsResult> GetAsync(string no)
    {
        await CheckGetPolicyAsync();
        if (!typeof(IHasNo).IsAssignableFrom(typeof(TEntity)) || no.IsNullOrWhiteSpace())
        {
            return default;
        }
        var entity = await (await Repository.GetQueryableAsync()).Where(r => ((IHasNo)r).No == no).FirstOrDefaultAsync();
        await AfterGetAsync(entity);
        var result = await MapToGetOutputDtoAsync(entity);
        try
        {
            await FullingResultDetailsAsync(result);
            return result;
        }
        catch
        {
            return default;
        }
    }

    public new virtual async Task<Dtos.PagedResult<TPagingListResult>> GetListAsync(TPagedParameter input)
    {
        var result = await base.GetListAsync(input);
        await FullingResultDetailsAsync(result.Items);
        if (PageListDesensitization)
        {
            await DesensitizationResultDetailsAsync(result.Items);
        }
        var page = new Dtos.PagedResult<TPagingListResult>(result.TotalCount, input.MaxResultCount, input.SkipCount, result.Items);
        return page;
    }

    public override async Task DeleteAsync(TKey id)
    {
        await CheckDeletePolicyAsync();

        var entity = await GetEntityByIdAsync(id);

        await ValidateDeleteAsync(entity);

        await BeforeDeleteAsync(entity);

        await DeleteByIdAsync(id);

        await AfterDeleteAsync(entity);

        await AddDeleteOperationLogAsync(entity);
    }

    protected async Task AddCreateOperationLogAsync(TEntity entity)
    {
        if (CreateOperationLogCreator == null) return;

        var (operation, message) = CreateOperationLogCreator(entity);

        await AddOperationLogAsync(operation, message, null, entity);
    }

    protected async Task AddUpdateOperationLogAsync(TEntity source, TEntity target)
    {
        if (UpdateOperationLogCreator == null) return;

        var (operation, message) = UpdateOperationLogCreator(source, target);

        await AddOperationLogAsync(operation, message, source, target);
    }

    protected async Task AddDeleteOperationLogAsync(TEntity entity)
    {
        if (DeleteOperationLogCreator == null) return;

        var (operation, message) = DeleteOperationLogCreator(new[] { entity });

        await AddOperationLogAsync(operation, message, entity, null);
    }

    protected async Task AddDeleteOperationLogAsync(IEnumerable<TEntity> entities)
    {
        if (DeleteOperationLogCreator == null) return;

        var (operation, message) = DeleteOperationLogCreator(entities);

        await AddOperationLogAsync(operation, message, entities, null);
    }

    protected async Task AddToggleEnableOperationLogAsync(TEntity entity, bool isEnabled)
    {
        if (ToggleEnableOperationLogCreator == null) return;

        var (operation, message) = ToggleEnableOperationLogCreator(entity);

        await AddOperationLogAsync(operation, message, !isEnabled, isEnabled);
    }

    protected virtual async Task AddOperationLogAsync(string operation, string message, string source, string target, IUser user = null)
    {
        await OperationLoggingManage.AddAsync(operation, message, source, target, user);
    }

    protected virtual async Task AddOperationLogAsync<TData>(string operation, string message, TData source, TData target, IUser user = null)
    {
        await AddOperationLogAsync(operation, message, source == null ? null : JsonSerializer.Serialize(source), target == null ? null : JsonSerializer.Serialize(target), user);
    }

    protected async Task FullingResultDetailsAsync(TDetailsResult result)
    {
        await FullingResultAuditingAsync(result);
        await FullingResultDetailsAsync(new[] { result });
    }

    protected async Task FullingResultDetailsAsync(IEnumerable<TPagingListResult> results)
    {
        await FullingResultAuditingAsync(results);
        await FullingResultDetailsAsync<TPagingListResult>(results);
    }

    protected virtual Task FullingResultDetailsAsync<TResult>(IEnumerable<TResult> results)
    {
        return Task.CompletedTask;
    }

    protected virtual Task DesensitizationResultDetailsAsync<TResult>(IEnumerable<TResult> results)
    {
        return Task.CompletedTask;
    }

    protected virtual async Task FullNameByRelationAsync<TRelationEntity, TResult, TRelationKey>(IEnumerable<TResult> results, Func<TResult, TRelationKey> keySelector, Action<TResult, string> nameSetter)
        where TRelationEntity : class, IEntity<TRelationKey>
    {
        var ids = results.Select(keySelector).Distinct();
        var repository = LazyInject<IXmsRepository<TRelationEntity, TRelationKey>>();
        var dict = (await (await repository.AsTrackingAsync()).Where(r => ids.Contains(r.Id)).ProjectTo<TRelationEntity, IdNameResult<TRelationKey>>().ToListAsync()).ToDictionary(r => r.Id, r => r.Name);
        foreach (var result in results)
        {
            var id = keySelector(result);
            if (dict.ContainsKey(id))
            {
                nameSetter(result, dict[id]);
            }
        }
    }

    protected virtual async Task FullNameByRelationAsync<TRelationEntity, TResult, TRelationKey>(TResult result, Func<TResult, TRelationKey> keySelector, Action<TResult, string> nameSetter)
        where TRelationEntity : class, IEntity<TRelationKey>
    {
        await FullNameByRelationAsync<TRelationEntity, TResult, TRelationKey>(new[] { result }, keySelector, nameSetter);
    }

    protected virtual Task FullingResultAuditingAsync(TDetailsResult result, bool isCreatorShowName = true, bool isLastModifierShowName = true, bool isReviewerShowName = true, bool isDeleterShowName = true, bool isMaskName = false)
    {
        return ReadOnlyAppServiceExtensions.FullingResultAuditingAsync(this, result, isCreatorShowName, isLastModifierShowName, isReviewerShowName, isDeleterShowName, isMaskName);
    }

    protected virtual Task FullingResultAuditingAsync(IEnumerable<TPagingListResult> results, bool isCreatorShowName = true, bool isLastModifierShowName = true, bool isReviewerShowName = true, bool isDeleterShowName = true, bool isMaskName = false)
    {
        return ReadOnlyAppServiceExtensions.FullingResultAuditingAsync(this, results, isCreatorShowName, isLastModifierShowName, isReviewerShowName, isDeleterShowName, isMaskName);
    }

    public virtual async Task<TDetailsResult> ToggleEnabledAsync(TKey id, bool isEnabled)
    {
        await CheckToggleEnablePolicyAsync();
        var entity = await this.GetEntityByIdAsync(id);
        if (entity is IHasEnabled entityEnable && entityEnable.IsEnabled != isEnabled)
        {
            await ValidateToggleEnableAsync(entity);
            entityEnable.IsEnabled = isEnabled;
            await BeforeToggleEnableAsync(entity, isEnabled);
            await Repository.UpdateAsync(entity);
            await AfterToggleEnableAsync(entity, isEnabled);
            await AddToggleEnableOperationLogAsync(entity, isEnabled);
        }
        var result = ObjectMapper.Map<TEntity, TDetailsResult>(entity);
        await FullingResultAuditingAsync(result);
        return result;
    }

    protected virtual async Task<List<TPagingListResult>> GetListByRelationAsync<TRelationEntity, TRelationKey>(
        Expression<Func<TRelationEntity, bool>> dataSelector,
        Expression<Func<TRelationEntity, TRelationKey>> outerKeySelector,
        Expression<Func<TEntity, TRelationKey>> innerKeySelector,
        Expression<Func<TRelationEntity, TEntity, TEntity>> resultSelector,
        bool desensitization = false)
        where TRelationEntity : class, IEntity
        where TRelationKey : IEquatable<TRelationKey>
    {
        var repository = LazyInject<IXmsRepository<TRelationEntity>>();
        var query = (await repository.GetQueryableAsync()).Where(dataSelector).Join((await ReadOnlyRepository.GetQueryableAsync()), outerKeySelector, innerKeySelector, resultSelector);
        var list = await AsyncExecuter.ToListAsync(query);
        var result = ObjectMapper.Map<List<TEntity>, List<TPagingListResult>>(list);
        await FullingResultDetailsAsync(result);
        if (desensitization)
        {
            await DesensitizationResultDetailsAsync(result);
        }
        return result;
    }

    protected virtual async Task CreateRelationAsync<TRelationEntity, TRelationKey>(TRelationKey[] relationKeys, Func<TRelationKey, TRelationEntity> dataCreator)
        where TRelationEntity : class, IEntity
        where TRelationKey : IEquatable<TRelationKey>
    {
        if (relationKeys == null || relationKeys.Length == 0) return;
        var repository = LazyInject<IXmsRepository<TRelationEntity>>();
        var adds = relationKeys.Select(dataCreator);
        await repository.InsertManyAsync(adds, true);
    }

    protected virtual async Task UpdateRelationAsync<TRelationEntity, TRelationKey>(
        TRelationKey[] relationKeys,
        Expression<Func<TRelationEntity, bool>> dataSelector,
        Func<TRelationEntity, TRelationKey> relationKeySelector,
        Func<TRelationKey, TRelationEntity> dataCreator)
        where TRelationEntity : class, IEntity
        where TRelationKey : IEquatable<TRelationKey>
    {
        relationKeys ??= Array.Empty<TRelationKey>();
        var repository = LazyInject<IXmsRepository<TRelationEntity>>();
        var rs = await AsyncExecuter.ToListAsync((await repository.GetQueryableAsync()).Where(dataSelector));
        var rks = rs.Select(relationKeySelector);
        var dict = rs.ToDictionary(relationKeySelector);
        var removes = rks.Except(relationKeys).Select(r => dict[r]);
        if (removes.Count() > 0)
        {
            await repository.DeleteManyAsync(removes, true);
        }
        var adds = relationKeys.Except(rks).ToArray();
        await CreateRelationAsync(adds, dataCreator);
    }

    protected virtual async Task DeleteRelationAsync<TRelationEntity>(Expression<Func<TRelationEntity, bool>> dataSelector)
        where TRelationEntity : class, IEntity
    {
        var repository = LazyInject<IXmsRepository<TRelationEntity>>();
        await repository.DeleteAsync(dataSelector);
    }

    protected virtual async Task ValidateCreateAsync(TCreateParameter input)
    {
        await Task.CompletedTask;
    }

    protected virtual async Task ValidateUpdateAsync(TEntity entity, TUpdateParameter input)
    {
        if (entity is IHasConcurrencyStamp ent && !ent.ConcurrencyStamp.IsNullOrWhiteSpace() && input is IHasConcurrencyStamp inp && !inp.ConcurrencyStamp.IsNullOrWhiteSpace() && ent.ConcurrencyStamp != inp.ConcurrencyStamp)
        {
            throw new UserFriendlyException("数据已被修改，请重新获取后进行修改！");
        }
        await Task.CompletedTask;
    }

    protected virtual async Task ValidateDeleteAsync(TEntity entity)
    {
        await Task.CompletedTask;
    }

    protected virtual async Task ValidateToggleEnableAsync(TEntity entity)
    {
        await Task.CompletedTask;
    }

    protected async Task<List<TEntity>> GetEntitiesByIdsAsync(IEnumerable<TKey> ids)
    {
        return await AsyncExecuter.ToListAsync((await Repository.GetQueryableAsync()).Where(r => ids.Contains(r.Id)));
    }

    public virtual async Task DeleteAsync(TKey[] ids)
    {
        await CheckDeleteManyPolicyAsync();

        var entities = await GetEntitiesByIdsAsync(ids);

        foreach (var entity in entities)
        {
            await ValidateDeleteAsync(entity);
            await BeforeDeleteAsync(entity);
        }
        await Repository.DeleteManyAsync(ids);
        foreach (var entity in entities)
        {
            await AfterDeleteAsync(entity);
        }

        await AddDeleteOperationLogAsync(entities);
    }

    public virtual async Task ImportAsync(string file)
    {
        await CheckImportPolicyAsync();
        var imports = await ExcelHelper.ReadAsync<TImportParameter>(file);
        var entities = ObjectMapper.Map<IEnumerable<TImportParameter>, List<TEntity>>(imports);
        await BeforeImportAsync(entities, imports);
        await Repository.InsertManyAsync(entities, true);
        await AfterImportAsync(entities, imports);
    }

    protected virtual Task<TExportResult> MapToExportDtoAsync(TEntity entity)
    {
        return Task.FromResult(MapToExportDto(entity));
    }

    protected virtual TExportResult MapToExportDto(TEntity entity)
    {
        return ObjectMapper.Map<TEntity, TExportResult>(entity);
    }

    protected override async Task<IQueryable<TEntity>> CreateFilteredQueryAsync(TPagedParameter input)
    {
        return await Repository.AsNoTrackingAsync();
    }

    public virtual async Task<(byte[], string)> ExportAsync(TPagedParameter input)
    {
        await CheckExportPolicyAsync();
        input.SkipCount = 0;
        input.MaxResultCount = ExportMaxResultCount;
        var query = await CreateFilteredQueryAsync(input);
        query = ApplySorting(query, input);
        query = ApplyPaging(query, input);
        var entities = await AsyncExecuter.ToListAsync(query);
        var entityDtos = new List<TExportResult>();
        foreach (var entity in entities)
        {
            entityDtos.Add(await MapToExportDtoAsync(entity));
        }
        await ReadOnlyAppServiceExtensions.FullingResultAuditingAsync(this, entityDtos, true, true, true, true, false);
        await FullingResultDetailsAsync(entityDtos);
        if (ExportDesensitization)
        {
            await DesensitizationResultDetailsAsync(entityDtos);
        }
        var fileName = typeof(TExportResult).GetCustomAttribute<SheetAttribute>()?.SheetName ?? "";
        fileName = fileName.IsNullOrWhiteSpace() ? "" : (fileName + "_");
        fileName += DateTime.Now.ToString("yyyyMMdd") + "_" + GuidGenerator.Create().ToString("n");
        var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "exports", DateTime.Now.ToString("yyyyMMdd"));
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }
        path = Path.Combine(path, fileName);
        await ExcelHelper.WriteAsync(path, entityDtos);
        var result = await File.ReadAllBytesAsync(path);
        return (result, fileName);
    }

    public virtual async Task<byte[]> TempletAsync()
    {
        return await ExcelHelper.WriteAsync(Array.Empty<TImportParameter>());
    }

    protected virtual async Task<bool> IsGrantPolicyAsync(string policyName)
    {
        return await AuthorizationService.IsGrantedAsync(policyName);
    }

    public virtual async Task<List<NameValue<TKey>>> GetNameValuesAsync(string filter = null)
    {
        if (typeof(TEntity).IsAssignableTo(typeof(IHasDisplayName<TKey>)))
        {
            var query = ((IQueryable<IHasDisplayName<TKey>>)(await Repository.AsNoTrackingAsync()))
                .WhereIf(!filter.IsNullOrWhiteSpace(), r => r.DisplayName.Contains(filter));
            if (typeof(TEntity).IsAssignableTo(typeof(IHasEnabled)))
            {
                query = query.Where(r => ((IHasEnabled)r).IsEnabled);
            }
            return await query.Select(r => new NameValue<TKey> { Name = r.DisplayName, Value = r.Id }).ToListAsync();
        }
        if (typeof(TEntity).IsAssignableTo(typeof(IHasName<TKey>)))
        {
            var query = ((IQueryable<IHasName<TKey>>)(await Repository.AsNoTrackingAsync()))
                .WhereIf(!filter.IsNullOrWhiteSpace(), r => r.Name.Contains(filter));
            if (typeof(TEntity).IsAssignableTo(typeof(IHasEnabled)))
            {
                query = query.Where(r => ((IHasEnabled)r).IsEnabled);
            }
            return await query.Select(r => new NameValue<TKey> { Name = r.Name, Value = r.Id }).ToListAsync();
        }
        return null;
    }

    public virtual async Task<List<NameValue>> GetNameNosAsync(string filter = null)
    {
        if (typeof(TEntity).IsAssignableTo(typeof(IHasNameNo)))
        {
            return await ((IQueryable<IHasNameNo>)(await Repository.AsNoTrackingAsync()))
                .WhereIf(!filter.IsNullOrWhiteSpace(), r => r.No == filter || r.Name.Contains(filter))
                .Select(r => new NameValue { Name = r.Name, Value = r.No }).ToListAsync();
        }
        return null;
    }
}
